package per.qiao.simplechian.spring;

import per.qiao.simplechian.core.DefaultNode;
import per.qiao.simplechian.core.NodeGroup;
import per.qiao.simplechian.core.NodePipeline;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.lang.Nullable;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 集成spring; 自动扫描责任链中的节点并组装成责任链, 其中每个节点的next和pre都是通过spring自动注入的, 每个节都都可以使用在不同的责任链中且互不影响
 *
 * @author: qiaoyp
 * @Date: 2024/8/21 21:55
 * @Description:
 */
@Configuration(proxyBeanMethods = false)
@Import(PipeLineForSpringConfig.class)
@Slf4j
public class PipeLineForSpringConfig implements ImportBeanDefinitionRegistrar, InitializingBean {

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private Map<String, NodePipeline<DefaultNode>> nodePipelineMap;

    @Autowired
    private Map<String, DefaultNode> defaultNodeMap;


    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Set<String> groupNames = this.getGroupNames(registry);
        for (String groupName : groupNames) {
            GenericBeanDefinition pipeLineBeanDefinition = new GenericBeanDefinition();
            pipeLineBeanDefinition.setBeanClass(NodePipeline.class);

            // 使用 ResolvableType 来处理泛型参数
//            ResolvableType resolvableType = ResolvableType.forClassWithGenerics(NodePipeline.class, DefaultNode.class);
//            resolvableType.getRawClass();
//            pipeLineBeanDefinition.setBeanClass(resolvableType.resolve().getClass());

            // 设置创建时使用的构造器
            ConstructorArgumentValues constructorArgumentValues = new ConstructorArgumentValues();
            constructorArgumentValues.addGenericArgumentValue(groupName, String.class.getName());
            pipeLineBeanDefinition.setConstructorArgumentValues(constructorArgumentValues);

            registry.registerBeanDefinition(groupName, pipeLineBeanDefinition);
        }
    }

    private Set<String> getGroupNames(BeanDefinitionRegistry registry) {
        String[] beanDefinitionNames = registry.getBeanDefinitionNames();
        Set<String> groupNames = new HashSet<>();
        for (String beanName : beanDefinitionNames) {
            BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
            if (!(beanDefinition instanceof AnnotatedBeanDefinition annotatedBeanDefinition)) {
                continue;
            }
            AnnotationMetadata metadata = annotatedBeanDefinition.getMetadata();
            // 检查类上是否存在NodeGroup注解
            if (this.hasNodeGroupAnnotation(metadata)) {
                // 将bean的作用域设置为prototype
                beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);

                this.extractGroupNames(groupNames, metadata);
            }
        }
        return groupNames;
    }

    private void extractGroupNames(Set<String> groupNames, AnnotationMetadata metadata) {
        Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(NodeGroup.class.getName());
        if (annotationAttributes == null) {
            return;
        }
        String[] tempGroupNames = (String[]) annotationAttributes.get("value");
        if (tempGroupNames != null && tempGroupNames.length > 0) {
            Collections.addAll(groupNames, tempGroupNames);
        }
    }

    private boolean hasNodeGroupAnnotation(AnnotationMetadata metadata) {
        return metadata.hasAnnotation(NodeGroup.class.getName());
    }

    @Override
    public void afterPropertiesSet() throws Exception {

        // group名称和bean的名称
        Map<String, List<String>> groups = new HashMap<>(this.defaultNodeMap.size());
        for (Map.Entry<String, DefaultNode> me : this.defaultNodeMap.entrySet()) {
            if (me.getValue().getClass().isAnnotationPresent(NodeGroup.class)) {
                NodeGroup annotation = me.getValue().getClass().getAnnotation(NodeGroup.class);
                String[] groupNames = this.getGroupNames(me.getValue(), annotation);
                if (groupNames == null) {
                    continue;
                }
                for (String groupName : groupNames) {
                    groups.computeIfAbsent(groupName, k -> new ArrayList<>()).add(me.getKey());
                }
            }
        }

        for (Map.Entry<String, NodePipeline<DefaultNode>> me : this.nodePipelineMap.entrySet()) {
            String nodePipeLineBeanName = me.getKey();
            List<String> nodeNames = groups.get(nodePipeLineBeanName);
            if (nodeNames == null || nodeNames.isEmpty()) {
                continue;
            }
            // getBean来生成多例
            NodePipeline<DefaultNode> nodePipeline = me.getValue();
            List<DefaultNode> defaultNodes = nodeNames.stream().map(e -> this.applicationContext.getBean(e, DefaultNode.class)).collect(Collectors.toList());
            nodePipeline.init(defaultNodes);
        }

        this.clear();
    }

    private void clear() {
        this.nodePipelineMap = null;
        this.defaultNodeMap = null;
    }

    @Nullable
    private String[] getGroupNames(DefaultNode node, NodeGroup annotation) {
        String[] groupNames = annotation.value();
        if (groupNames == null || groupNames.length == 0) {
            log.warn("责任链节点缺少@NodeGroup.value属性: {}", node.getClass().getName());
            return null;
        }
        if (groupNames.length > 1) {
            groupNames = Arrays.stream(groupNames).distinct().toArray(String[]::new);
        }
        return groupNames;
    }
}
